package org.kamionowski.supreme.jobs;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.task.TaskRejectedException;
import org.springframework.stereotype.Component;

import java.util.LinkedList;
import java.util.List;
import java.util.PriorityQueue;
import java.util.Queue;
import java.util.concurrent.Future;

/**
 * Created by IntelliJ IDEA.
 * User: Marcin Kamionowski
 * Date: 11.04.11
 * Time: 22:30
 */
@Component
public class CrawlerManagerImpl implements CrawlerManager{

    @Autowired
    private CrawlerJob job;

    private final Queue<ScanContext> queued;
    private final List<ScanContext> inProgress;
    private final List<ScanContext> ended;
    private final JobEndListener listener = new CrawlerListener(this);
    private final int historySize = 10;

    public CrawlerManagerImpl() {
        final int queueSize = 20;
        queued = new PriorityQueue<ScanContext>(queueSize, new PrioritizeScanContext());
        inProgress = new LinkedList<ScanContext>();
        ended = new LinkedList<ScanContext>();
    }

    private final static class CrawlerListener implements JobEndListener{

        private CrawlerManagerImpl manager;

        private CrawlerListener(CrawlerManagerImpl manager) {
            this.manager = manager;
        }

        @Override
        public void jobEnd(final ScanContext context) {
            synchronized (manager){
                manager.jobEnded(context);
            }
        }
    }

    @Override
    public synchronized ScanResponse scanService(ScanRequest request) {
        ScanContext context = new ScanContext(request);

        if(exists(context)){
            return ScanResponse.EXISTS;
        }
        context.addListener(listener);
        try{
            Future<Boolean> result = job.doScanning(context);
            inProgress.add(context);
            return ScanResponse.IN_PROGRESS;
        }catch(TaskRejectedException exc){
            queued.add(context);
            return ScanResponse.QUEUED;
        }
    }

    private void jobEnded(ScanContext context){
        this.ended.add(context);
        this.inProgress.remove(context);
        cleanHistory();
    }

    private void cleanHistory(){
        while(this.ended.size() > historySize){
            this.ended.remove(historySize);
        }
    }

    private boolean exists(ScanContext newJobContext){
        return queued.contains(newJobContext) || inProgress.contains(newJobContext);
    }


}

